// 定义Trie树的节点类型，每个节点可以包含子节点或一个布尔值表示是否为敏感词的结尾
type TrieNode = {
  [char: string]: TrieNode | boolean
}

// DfaFilter 类用于敏感词过滤，支持DFA（确定有限状态自动机）匹配、正则表达式匹配和模糊匹配。
export class DfaFilter {
  // Trie树的根节点
  private root: TrieNode = {}
  // 正则表达式对象，用于正则表达式匹配模式
  private regex: RegExp | null = null
  // 指示是否使用正则表达式进行匹配
  private useRegex: boolean
  // 指示是否使用模糊匹配（例如，允许大小写不敏感或中间插入字符）
  private useFuzzy: boolean
  // 存储敏感词列表
  private sensitiveWords: string[]

  /**
   * 构造函数，初始化DfaFilter实例。
   * @param sensitiveWords 敏感词数组。
   * @param useRegex 是否使用正则表达式模式。如果为true，则将敏感词构建为正则表达式；否则构建为Trie树。
   * @param useFuzzy 是否使用模糊匹配。仅当useRegex为true时有效，允许在正则表达式中进行模糊匹配。
   */
  constructor(sensitiveWords: string[], useRegex: boolean = false, useFuzzy: boolean = false) {
    this.sensitiveWords = sensitiveWords
    this.useRegex = useRegex
    this.useFuzzy = useFuzzy
    if (useRegex) {
      this.buildRegex()
    } else {
      this.buildTrie()
    }
  }

  // 构建Trie树（前缀树），用于高效地存储和查找敏感词。
  private buildTrie() {
    for (const word of this.sensitiveWords) {
      let node = this.root
      for (const char of word) {
        if (!node[char]) {
          node[char] = {}
        }
        node = node[char] as TrieNode
      }
      // 标记当前节点是一个敏感词的结尾
      node['isEnd'] = true
    }
  }

  // 构建正则表达式，用于敏感词匹配。
  private buildRegex() {
    const patterns = this.sensitiveWords.map((word) => {
      // 转义正则表达式中的特殊字符
      let w = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
      if (this.useFuzzy) {
        // 支持模糊匹配：大小写不敏感、允许中间插入少量字符（如同音字、变体等）
        w = w.split('').join('.{0,2}')
      }
      return w
    })
    // 根据是否使用模糊匹配，创建不同的正则表达式标志（'gi'表示全局和不区分大小写，'g'表示全局）
    this.regex = new RegExp(patterns.join('|'), this.useFuzzy ? 'gi' : 'g')
  }

  /**
   * 检测文本中是否包含敏感词。
   * @param text 要检测的文本。
   * @returns 包含所有检测到的敏感词的数组。
   */
  public detect(text: string): string[] {
    if (this.useRegex && this.regex) {
      const matches = text.match(this.regex)
      // 返回去重后的匹配结果
      return matches ? Array.from(new Set(matches)) : []
    }
    // DFA 检测
    const matches: string[] = []
    for (let i = 0; i < text.length; i++) {
      let node = this.root
      let j = i
      let matched = ''
      // 遍历文本，在Trie树中查找匹配的敏感词
      while (j < text.length && node[text[j]]) {
        matched += text[j]
        node = node[text[j]] as TrieNode
        j++
        if (node['isEnd']) {
          matches.push(matched)
          break
        }
      }
    }
    return matches
  }

  /**
   * 替换文本中的敏感词。
   * @param text 要处理的文本。
   * @returns 敏感词被替换后的文本。
   */
  public replace(text: string): string {
    if (this.useRegex && this.regex) {
      // 使用正则表达式替换敏感词为星号
      return text.replace(this.regex, (match) => '*'.repeat(match.length))
    }
    let result = text
    // 检测文本中的敏感词
    const hits = this.detect(text)
    for (const word of hits) {
      // 将敏感词替换为相同长度的'×'字符
      const stars = '×'.repeat(word.length)
      result = result.replace(new RegExp(word, 'g'), stars)
    }
    return result
  }
}
